package com.inepex.inechart.client.linechart;

import java.util.ArrayList;
import java.util.List;
import java.util.TreeMap;

import com.inepex.inechart.client.IneChartEventManager;
import com.inepex.inechart.client.axes.AxesGWT;
import com.inepex.inechart.client.axes.IncrementalAxisDrawer.DrawingAreaProviderForAxis;
import com.inepex.inechart.client.cartesian.CartesianChartModuleGWT;
import com.inepex.inechart.client.cartesian.CartesianJobScheduler;
import com.inepex.inechart.client.displayengine.DisplayEngine;
import com.inepex.inechart.client.jobscheduler.HasScheduledJob;
import com.inepex.inechart.shared.CartesianException;
import com.inepex.inechart.shared.axes.Axis;
import com.inepex.inechart.shared.linechart.Curve;
import com.inepex.inechart.shared.linechart.LineChart;
import com.inepex.inechart.shared.linechart.PointFilter;
import com.inepex.inechart.shared.properties.Color;
import com.inepex.inegraphics.impl.client.DrawingAreaGWT;
import com.inepex.inegraphics.shared.GraphicalObjectContainer;

public class LineChartGwt extends CartesianChartModuleGWT implements LineChart {
	
	private final static String layerGroup = "layerGroup:";
	private final static String lineChartGOs = layerGroup + "LineChartGOs";
	private final static String pointChartGOs = layerGroup + "PointChartGOs";
	private final static String axisLayerGroup = layerGroup + "Axes";
	private final static String axisAndTickLinesLayer = "axisAndTickLinesGOs";
	private final static String tickTextLayer = "tickTexts";
	private final static String gridAndGridFillsLayer = "gridAndGridFillsLayer";

	protected LineChartModelAssistGWT lineChartModelAssist;
	protected ArrayList<Curve> curves;
	protected TreeMap<Curve, CurveModelContainer> curveModelMap;
	protected TreeMap<Curve, LineChartToGoGWT> curveToGOMap;
	protected double xMin, xMax, yMin, yMax;

	public LineChartGwt(AxesGWT axes, IneChartEventManager eventManager,
			DisplayEngine displayEngine) {
		super(axes, eventManager, displayEngine);
		
		curves = new ArrayList<Curve>();
		lineChartModelAssist = new LineChartModelAssistGWT(this);
		curveModelMap = new TreeMap<Curve, CurveModelContainer>();
		curveToGOMap = new TreeMap<Curve, LineChartToGoGWT>();
		setPointFilter(new PointFilter());
	}

	/* CartesianJob implementations */
	@Override
	protected void clear(HasScheduledJob jobScheduler) {
		for (Curve curve : curves) {
			lineChartModelAssist.clearCurve(curve);
			curveModelMap.get(curve).curveParts.clear();
		}
		super.clear(jobScheduler);
	}

	@Override
	public GraphicalObjectContainer update() {
		if (curves == null || curves.size() == 0){
			try {
				cartesianJobScheduler.startJobSequence(CartesianJobScheduler.clearOnly);
			} catch (CartesianException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		else {
			if (autoScaleViewportHorizontal || autoScaleViewportVertical) {
				yMin = Double.MAX_VALUE;
				yMax = -Double.MAX_VALUE;
				xMin = Double.MAX_VALUE;
				xMax = -Double.MAX_VALUE;
				for (Curve c : curves) {
					if (c.getDataSet().getKeyMax().getValuePair()[0] > xMax){
						xMax = c.getDataSet().getKeyMax().getValuePair()[0];
					}
					if (c.getDataSet().getValueMax().getValuePair()[1] > yMax){
						yMax = c.getDataSet().getValueMax().getValuePair()[1];
					}
					if (c.getDataSet().getKeyMin().getValuePair()[0] < xMin){
						xMin = c.getDataSet().getKeyMin().getValuePair()[0];
					}
					if (c.getDataSet().getValueMin().getValuePair()[1] < yMin){
						yMin = c.getDataSet().getValueMin().getValuePair()[1];
					}
				}
				if(yMin == yMax){
					yMax++;
					yMin--;
				}
				if(xMin == xMax){
					xMax++;
					xMin--;
				}
				if(autoScaleViewportHorizontal && cartesianAssist.getxAxis().getModulToAlign() == this){
					cartesianAssist.getxAxis().setMax(xMax);
					cartesianAssist.getxAxis().setMin(xMin);
				}
				if(autoScaleViewportVertical && cartesianAssist.getyAxis().getModulToAlign() == this){
					cartesianAssist.getyAxis().setMax(yMax);
					cartesianAssist.getyAxis().setMin(yMin);	
				}
				updateCartesianAssist();
			}
			setActualJobTileToBase();
			try {
				cartesianJobScheduler.startJobSequence(CartesianJobScheduler.resolutionChangeUpdateJobSequence);
			} catch (CartesianException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return null;
	}

	@Override
	protected void prepareModel(final HasScheduledJob jobScheduler) {
		new IncrementalLineChartPointCalculator(CartesianJobScheduler.defaultUpdatePartTreshold, curves, this, actualJobTile.getxFrom(), actualJobTile.getxTo()) {
			@Override
			public void onFinished() {
				jobScheduler.onJobFinish();
			}
		};
	}

	@Override
	protected void createModelGOs(final HasScheduledJob jobScheduler) {
		new IncrementalLineChartGOCreator(CartesianJobScheduler.defaultUpdatePartTreshold, this, curves, actualJobTile) {
			@Override
			public void onFinished() {
				jobScheduler.onJobFinish();
			}
		};
	}

	@Override
	protected void draw(final HasScheduledJob jobScheduler) {
		for (Curve curve : curves) {
			alignGraphicalObjectsToActualJobTile(curveToGOMap.get(curve).lineChartGOs);
			alignGraphicalObjectsToActualJobTile(curveToGOMap.get(curve).pointChartGOs);
		}
		new IncrementalLineChartDrawer(this, curves, CartesianJobScheduler.defaultUpdatePartTreshold) {
			@Override
			public void onFinish() {
//				jobScheduler.onJobFinish();
				drawAxes(jobScheduler);
			}
		};
	}
	
	protected void drawAxes(final HasScheduledJob jobScheduler){
		axes().drawAxes(getAllAxesAlignedToThisModule(), jobScheduler,  CartesianJobScheduler.defaultUpdatePartTreshold, new DrawingAreaProviderForAxis() {
			
			@Override
			public DrawingAreaGWT getTickTextDrawingAreaForAxis(Axis axis) {
				return getOrCreateLayerForActualTile(tickTextLayer, axisLayerGroup);
			}
			
			@Override
			public DrawingAreaGWT getGridAndFillDrawingAreaForAxis(Axis axis) {
				return getOrCreateLayerForActualTile(gridAndGridFillsLayer, axisLayerGroup);
			}
			
			@Override
			public DrawingAreaGWT getAxisAndTickLineDrawingAreaForAxis(Axis axis) {
				return getOrCreateLayerForActualTile(axisAndTickLinesLayer, axisLayerGroup);
			}
		});
	}
	
	protected DrawingAreaGWT getOrCreateLayerLCLayerForActualTile(Curve curve){
		return getOrCreateLayerForActualTile(getLineChartGOsLayerGroupName(curve), getCurveLayerGroupName(curve));
	}
	
	protected DrawingAreaGWT getOrCreateLayerPCLayerForActualTile(Curve curve){
		return getOrCreateLayerForActualTile(getPointChartGOsLayerGroupName(curve), getCurveLayerGroupName(curve));
	}
	
	protected DrawingAreaGWT getAxesLayerForActualTile(){
		return getOrCreateLayerForActualTile("defaultAxes", axisLayerGroup); //TODO
	}
		
	private String getCurveLayerGroupName(Curve curve){
		return layerGroup + curve.toString();
	}
	
	private String getLineChartGOsLayerGroupName(Curve curve){
		return lineChartGOs + "_" + curve.toString();
	}
	
	private String getPointChartGOsLayerGroupName(Curve curve){
		return pointChartGOs + "_" + curve.toString();
	}

	/* property methods */
	@Override
	public TreeMap<String, Color> getLegendEntries() {
		if(legendEntries == null){
			TreeMap<String, Color> entries = new TreeMap<String, Color>();
			for(Curve c : curves){
				entries.put(c.getDataSet().getTitle(),
						c.getLineProperties() != null && c.getLineProperties().getLineColor() != null ?
								c.getLineProperties().getLineColor() :
									c.getDataSet().getColor());
			}
			return entries;
		}
		else{
			return legendEntries;
		}
	}

	@Override
	public PointFilter getPointFilter() {
		return lineChartModelAssist.getPointFilter();
	}

	@Override
	public void setPointFilter(PointFilter pointFilter) {
		lineChartModelAssist.setPointFilter(pointFilter);
	}

	/* LineChart iface */
	@Override
	public List<Curve> getCurves() {
		return curves;
	}

	@Override
	public void addCurve(Curve curve) {
		if (curve == null){
			return;
		}
		curves.add(curve);
		curveModelMap.put(curve, new CurveModelContainer(curve));
		curveToGOMap.put(curve, new LineChartToGoGWT(this, curve));
	}

	@Override
	public void removeCurve(Curve curve) {
		curves.remove(curve);
		curveModelMap.remove(curve);
		curveToGOMap.remove(curve);
	}

}
